home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 2325 / 2325.xpi / chrome / content / ticker.object.js < prev   
Text File  |  2009-11-24  |  48KB  |  1,631 lines

  1. var RSSTICKER = {
  2.     livemarkService : Components.classes["@mozilla.org/browser/livemark-service;2"].getService(Components.interfaces.nsILivemarkService),
  3.     bookmarkService : Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Components.interfaces.nsINavBookmarksService),
  4.     ioService : Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService),
  5.     
  6.     ignoreFilename : "rss-ticker.ignore.txt",
  7.     
  8.     strings : null,
  9.     
  10.     profilePath : null,
  11.     
  12.     tickTimer : null,
  13.     internalPause : false,
  14.     
  15.     /*****************************
  16.      * Default preference values *
  17.      *****************************/
  18.     
  19.     // Disable ticker
  20.     disabled : false,
  21.     
  22.     // Should the ticker be hidden when empty?
  23.     hideWhenEmpty : false,
  24.     
  25.     // Speed of the ticker: 1 is fastest, a million is the slowest (not really,
  26.     // but it would be close
  27.     tickSpeed : 12,
  28.     
  29.     // How often are the feeds updated (in minutes)?
  30.     updateFrequency : 30,
  31.     
  32.     // Should the ticker items be shuffled?
  33.     randomizeItems : false,
  34.         
  35.     // Number of ticks that it takes to remove one item completely
  36.     // Set higher for a smoother scroll
  37.     ticksPerItem : 200,
  38.     
  39.     hideVisited : true,
  40.     boldUnvisited : false,
  41.     
  42.     // Item width options
  43.     
  44.     displayWidth : {
  45.         // Should there be a limit for item width?
  46.         limitWidth : true,
  47.         
  48.         // Should the width set a max width or a width?
  49.         isMaxWidth : false,
  50.         
  51.         // Width in pixels
  52.         itemWidth : 250
  53.     },
  54.     
  55.     // Context Menu options
  56.     cmOptions : {
  57.         open : true,
  58.         openInTab : true,
  59.         openInWindow : false,
  60.         
  61.         openAllInTabs : true,
  62.         openUnreadInTabs : false,
  63.         openFeedInTabs : true,
  64.         openFeedUnreadInTabs : false,
  65.         
  66.         copyLinkTitle : false,
  67.         copyLinkURL : true,
  68.         
  69.         refreshFeeds : true,
  70.         manageFeeds : true,
  71.         
  72.         markAsRead : true,
  73.         markFeedAsRead : true,
  74.         markAllAsRead : true,
  75.         
  76.         options : true,
  77.         disableTicker : false
  78.     },
  79.     
  80.     // Always open in new tab
  81.     alwaysOpenInNewTab : false,
  82.     
  83.     // Limit number of items per feed
  84.     limitItemsPerFeed : false,
  85.     itemsPerFeed : 5, 
  86.     
  87.     /*****************************
  88.      * End def preference values *
  89.      *****************************/
  90.     
  91.     // True when the mouse is over the ticker
  92.     mouseOverFlag : false,
  93.     
  94.     // Reference to the actual physical toolbar
  95.     toolbar : null,
  96.     
  97.     // Reference to container for toolbar and other info
  98.     ticker : null,
  99.     
  100.     // Toggles on/off debugging messages in the console.
  101.     DEBUG : false,
  102.     
  103.     // Button on the toolbar that shows status messages when the feeds are loading
  104.     loadingNotice : null,
  105.     
  106.     // Current width of the first feed item (the one that is being shrunk)
  107.     currentFirstItemMargin : 0,
  108.     
  109.     tickLength : 0,
  110.     
  111.     lastOneRiotTimestamp : 0,
  112.     
  113.     onload : function () {
  114.         this.loadPrefs();
  115.         
  116.         var db = this.getDB();
  117.         
  118.         if (!db.tableExists("history")) {
  119.             db.executeSimpleSQL("CREATE TABLE IF NOT EXISTS history (id TEXT PRIMARY KEY, date INTEGER)");
  120.         }
  121.         
  122.         this.strings = document.getElementById("RSSTICKER-bundle");
  123.         this.customizeContextMenus();
  124.         
  125.         this.ticker = this.ce('toolbar');
  126.         this.ticker.setAttribute("id","RSSTICKER" + this.strings.getString("toolbar"));
  127.         this.ticker.setAttribute("class","chromeclass-toolbar");
  128.         this.ticker.setAttribute("hidden",false);
  129.         this.ticker.setAttribute("iconsize","small");
  130.         this.ticker.setAttribute("inherits","collapsed,hidden");
  131.         this.ticker.setAttribute("mode","full");
  132.         this.ticker.setAttribute("persist","collapsed,hidden");
  133.         this.ticker.setAttribute("toolbarname",this.strings.getString("extension.name"));
  134.         this.ticker.style.maxHeight = '24px';
  135.     
  136.         this.toolbar = this.ce('hbox');
  137.         this.toolbar.spacer = this.ce('spacer');
  138.         this.toolbar.appendChild(this.toolbar.spacer);
  139.         this.toolbar.style.maxHeight = '24px';
  140.     
  141.         this.ticker.setAttribute("contextmenu","RSSTICKERCM");
  142.     
  143.         this.ticker.setAttribute("onmouseover","RSSTICKER.mouseOverFlag = true;");
  144.         this.ticker.setAttribute("onmouseout","RSSTICKER.mouseOverFlag = false;");
  145.  
  146.         document.getElementById("RSSTICKERItemCM").setAttribute("onmouseover","RSSTICKER.mouseOverFlag = true;");
  147.         document.getElementById("RSSTICKERItemCM").setAttribute("onmouseout","RSSTICKER.mouseOverFlag = false;");
  148.     
  149.         this.ticker.appendChild(this.toolbar);
  150.         
  151.         this.loadingNoticeParent = this.ce('toolbaritem');
  152.         this.loadingNoticeParent.setAttribute("tooltip","RSSTICKERLoadingNoticeTooltip");
  153.         this.loadingNoticeParent.id = "RSSTICKER-throbber-box";
  154.         this.loadingNoticeParent.setAttribute("title","RSS Ticker Activity Indicator");
  155.         this.loadingNoticeParent.setAttribute("align","center");
  156.         this.loadingNoticeParent.setAttribute("pack","center");
  157.     
  158.         this.loadingNotice = this.ce('image');
  159.         this.loadingNotice.setAttribute("src","chrome://rss-ticker/content/skin-common/throbber.gif");
  160.         this.loadingNotice.id = "RSSTICKER-throbber";
  161.         this.loadingNotice.setAttribute("busy","false");
  162.         this.loadingNotice.style.marginRight = '4px';
  163.         this.loadingNoticeParent.appendChild(this.loadingNotice);
  164.     
  165.         try {
  166.             document.getElementById("nav-bar").appendChild(this.loadingNoticeParent);
  167.         } catch (e) {
  168.             if (this.DEBUG) this.logMessage(e);
  169.         }
  170.         
  171.         if (this.prefs.getBoolPref("disabled")){
  172.             this.disable();
  173.             return;
  174.         }
  175.         else {
  176.             this.enable();
  177.         }
  178.         
  179.         setTimeout(function () { RSSTICKER.showFirstRun(); }, 1500);
  180.     },
  181.     
  182.     showFirstRun : function () {
  183.         var version = Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager).getItemForID("{1f91cde0-c040-11da-a94d-0800200c9a66}").version;
  184.  
  185.         if (RSSTICKER.prefs.getCharPref("lastVersion") != version) {
  186.             RSSTICKER.prefs.setCharPref("lastVersion",version);
  187.             var theTab = gBrowser.addTab("http://www.chrisfinke.com/firstrun/rss-ticker.php");
  188.             gBrowser.selectedTab = theTab;
  189.         }
  190.         
  191.         // Ask if they want the trending terms feed.
  192.         
  193.         if (!this.prefs.getBoolPref("trendRequest")) {
  194.             this.prefs.setBoolPref("trendRequest", true);
  195.             
  196.             setTimeout(
  197.                 function () {
  198.                     window.openDialog("chrome://rss-ticker/content/one-riot-suggestion.xul", "trends", "chrome,dialog,centerscreen,titlebar,alwaysraised,modal");
  199.                 }, 5000
  200.             );
  201.         }
  202.     },
  203.         
  204.     observe: function(subject, topic, data) {
  205.         if (topic != "nsPref:changed") {
  206.             return;
  207.         }
  208.         
  209.         switch(data) {
  210.             case "disabled":
  211.                 if (this.prefs.getBoolPref("disabled")) {
  212.                     this.disable();
  213.                 }
  214.                 else {
  215.                     this.enable();
  216.                 }
  217.             break;
  218.             case "hideWhenEmpty":
  219.                 this.hideWhenEmpty = this.prefs.getBoolPref("hideWhenEmpty");
  220.             break;
  221.             case "tickerPlacement":
  222.                 this.attachTicker();
  223.             break;
  224.             case "randomizeItems":
  225.                 this.randomizeItems = this.prefs.getBoolPref("randomizeItems");
  226.             break;
  227.             case "limitItemsPerFeed":
  228.                 this.limitItemsPerFeed = this.prefs.getBoolPref("limitItemsPerFeed");
  229.             break;
  230.             case "itemsPerFeed":
  231.                 if (this.prefs.getIntPref("itemsPerFeed") < 0) {
  232.                     this.prefs.setIntPref("itemsPerFeed", 0);
  233.                 }
  234.                 else {
  235.                     this.limitItemsPerFeed = this.prefs.getIntPref("itemsPerFeed");
  236.                 }
  237.             break;
  238.             case "alwaysOpenInNewTab":
  239.                 this.alwaysOpenInNewTab = this.prefs.getBooPref("alwaysOpenInNewTab");
  240.             break;
  241.             case "tickSpeed":
  242.                 this.tickLength = this.prefs.getIntPref("tickSpeed") * (500 / this.ticksPerItem);
  243.             break;
  244.             case "updateFrequency":
  245.                 RSSTICKER.setReloadInterval(this.prefs.getIntPref("updateFrequency"));
  246.             break;
  247.             case "ticksPerItem":
  248.                 this.ticksPerItem = this.prefs.getIntPref("ticksPerItem");
  249.                 this.tickLength = this.prefs.getIntPref("tickSpeed") * (500 / this.ticksPerItem);
  250.             break;
  251.             case "dw.limitWidth":
  252.                 this.displayWidth.limitWidth = this.prefs.getBoolPref("dw.limitWidth");
  253.             break;
  254.             case "dw.isMaxWidth":
  255.                 this.displayWidth.isMaxWidth = this.prefs.getBoolPref("dw.isMaxWidth");
  256.             break;
  257.             case "smoothness":
  258.                 if (this.prefs.getIntPref("smoothness") <= 0) {
  259.                     this.prefs.setIntPref("smoothness", 1);
  260.                 }
  261.             break;
  262.             case "updateToggle":
  263.                 this.updateAllFeeds();
  264.             break;
  265.         }
  266.         
  267.         this.customizeContextMenus();
  268.         this.checkForEmptiness();
  269.     },
  270.     
  271.     attachTicker : function () {
  272.         if (this.ticker.parentNode) {
  273.             this.ticker.parentNode.removeChild(this.ticker);
  274.         }
  275.         
  276.         var tickerPlacement = this.prefs.getIntPref("tickerPlacement");
  277.         
  278.         if (tickerPlacement == 1){
  279.             // Beneath the status bar
  280.             document.getElementById('browser-bottombox').insertBefore(this.ticker, document.getElementById('status-bar').nextSibling);
  281.             if (this.DEBUG) this.logMessage("Placed after status bar.");
  282.         }
  283.         else if (tickerPlacement == 2){
  284.             // Up by the Bookmarks Toolbar
  285.             document.getElementById('navigator-toolbox').appendChild(this.ticker);
  286.             if (this.DEBUG) this.logMessage("Placed in navigator toolbox.");
  287.         }
  288.     },
  289.     
  290.     enable : function () {
  291.         this.disabled = false;
  292.         
  293.         if (document.getElementById("RSSTICKER-button")){
  294.             document.getElementById("RSSTICKER-button").setAttribute("greyed","false");
  295.         }
  296.         
  297.         this.attachTicker();
  298.         this.startFetchingFeeds();
  299.     },
  300.     
  301.     disable : function () {
  302.         if (this.DEBUG) this.logMessage("Ticker disabled.");
  303.         
  304.         this.disabled = true;
  305.         
  306.         if (this.ticker.parentNode){
  307.             this.ticker.parentNode.removeChild(this.ticker);
  308.         }
  309.         
  310.         while (this.toolbar.childNodes.length > 0){
  311.             this.toolbar.removeChild(this.toolbar.lastChild);
  312.         }
  313.         
  314.         this.toolbar.appendChild(this.toolbar.spacer);
  315.         
  316.         if (document.getElementById("RSSTICKER-button")) {
  317.             document.getElementById("RSSTICKER-button").setAttribute("greyed","true");
  318.         }
  319.         
  320.         this.prefs.setIntPref("lastUpdate", 0);
  321.         this.stopFetchingFeeds();
  322.     },
  323.     
  324.     loadingNoticeTimeout : null,
  325.     
  326.     addLoadingNotice : function (message) {
  327.         clearTimeout(RSSTICKER.loadingNoticeTimeout);
  328.         
  329.         if (!message || this.disabled) {
  330.             this.loadingNotice.setAttribute("busy", "false");
  331.         }
  332.         else {
  333.             if (this.DEBUG) this.logMessage("Setting loading notice.");
  334.             
  335.             var text = document.createTextNode(message);
  336.             
  337.             while (document.getElementById("RSSTICKERLoadingNoticeText").childNodes.length > 0){
  338.                 document.getElementById("RSSTICKERLoadingNoticeText").removeChild(document.getElementById("RSSTICKERLoadingNoticeText").lastChild);
  339.             }
  340.             
  341.             document.getElementById("RSSTICKERLoadingNoticeText").appendChild(text);
  342.             
  343.             this.loadingNotice.setAttribute("busy", "true");
  344.         }
  345.         
  346.         RSSTICKER.loadingNoticeTimeout = setTimeout(function () { RSSTICKER.addLoadingNotice(); }, 5000);
  347.     },
  348.     
  349.     loadPrefs : function () {
  350.         if (this.DEBUG) this.logMessage("Loading prefs.");
  351.     
  352.         this.prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("extensions.rssticker.");
  353.         this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
  354.         this.prefs.addObserver("", this, false);
  355.     
  356.         this.disabled = this.prefs.getBoolPref("disabled");
  357.         this.hideWhenEmpty = this.prefs.getBoolPref("hideWhenEmpty");
  358.         this.randomizeItems = this.prefs.getBoolPref("randomizeItems");
  359.         this.ticksPerItem = this.prefs.getIntPref("ticksPerItem");
  360.         this.displayWidth.itemWidth = this.prefs.getIntPref("dw.itemWidth");
  361.         this.limitItemsPerFeed = this.prefs.getBoolPref("limitItemsPerFeed");
  362.         this.itemsPerFeed = this.prefs.getIntPref("itemsPerFeed");
  363.         this.boldUnvisited = this.prefs.getBoolPref("boldUnvisited");
  364.         this.hideVisited = this.prefs.getBoolPref("hideVisited");
  365.         this.alwaysOpenInNewTab = this.prefs.getBoolPref("alwaysOpenInNewTab");
  366.         this.displayWidth.limitWidth = this.prefs.getBoolPref("dw.limitWidth");
  367.         this.displayWidth.isMaxWidth = this.prefs.getBoolPref("dw.isMaxWidth");
  368.         
  369.         this.tickLength = this.prefs.getIntPref("tickSpeed") * (500 / this.prefs.getIntPref("ticksPerItem"));
  370.     },
  371.     
  372.     customizeContextMenus : function () {
  373.         this.cmOptions.open = this.prefs.getBoolPref("cm.open");
  374.         this.cmOptions.openInTab = this.prefs.getBoolPref("cm.openInTab");
  375.         this.cmOptions.openInWindow = this.prefs.getBoolPref("cm.openInWindow");
  376.         this.cmOptions.openAllInTabs = this.prefs.getBoolPref("cm.openAllInTabs");
  377.         this.cmOptions.openUnreadInTabs = this.prefs.getBoolPref("cm.openUnreadInTabs");
  378.         this.cmOptions.openFeedInTabs = this.prefs.getBoolPref("cm.openFeedInTabs");
  379.         this.cmOptions.openFeedUnreadInTabs = this.prefs.getBoolPref("cm.openFeedUnreadInTabs");
  380.         this.cmOptions.copyLinkTitle = this.prefs.getBoolPref("cm.copyLinkTitle");
  381.         this.cmOptions.copyLinkURL = this.prefs.getBoolPref("cm.copyLinkURL");
  382.         this.cmOptions.refreshFeeds = this.prefs.getBoolPref("cm.refreshFeeds");
  383.         this.cmOptions.manageFeeds = this.prefs.getBoolPref("cm.manageFeeds");
  384.         this.cmOptions.markAsRead = this.prefs.getBoolPref("cm.markAsRead");
  385.         this.cmOptions.markFeedAsRead = this.prefs.getBoolPref("cm.markFeedAsRead");
  386.         this.cmOptions.markAllAsRead = this.prefs.getBoolPref("cm.markAllAsRead");
  387.         this.cmOptions.options = this.prefs.getBoolPref("cm.options");
  388.         this.cmOptions.disableTicker = this.prefs.getBoolPref("cm.disableTicker");
  389.             
  390.         this.customizeContextMenu("RSSTICKERItemCM");
  391.         this.customizeContextMenu("RSSTICKERCM");
  392.         this.customizeContextMenu("RSSTICKERButtonCM");
  393.     },
  394.     
  395.     customizeContextMenu : function(menuID){
  396.         var menu = document.getElementById(menuID);
  397.         var separator = null;
  398.         var firstOption = false;
  399.         
  400.         for (var i = 0; i < menu.childNodes.length; i++){
  401.             var option = menu.childNodes[i];
  402.             
  403.             if (option.nodeName == 'menuitem'){
  404.                 if (RSSTICKER.cmOptions[option.getAttribute("option")] == false){
  405.                     option.style.display = 'none';
  406.                 }
  407.                 else {
  408.                     option.style.display = '';
  409.                     firstOption = true;
  410.                     
  411.                     if (separator) {
  412.                         separator.style.display = '';
  413.                     }
  414.                     
  415.                     separator = null;
  416.                 }
  417.             }
  418.             else {
  419.                 option.style.display = 'none';
  420.                 
  421.                 if (firstOption && !separator){
  422.                     separator = option;
  423.                 }
  424.             }
  425.         }
  426.     },
  427.     
  428.     unload : function () {
  429.         this.prefs.setIntPref("lastUpdate", 0);
  430.     },
  431.     
  432.     feedsToFetch : [],
  433.     feedIndex : 0,
  434.     feedUpdateTimeout : null,
  435.     secondsBetweenFeeds : 0,
  436.     
  437.     stopFetchingFeeds : function () {
  438.         clearTimeout(RSSTICKER.feedUpdateTimeout);
  439.         
  440.         this.feedsToFetch = [];
  441.         this.feedIndex = 0;
  442.     },
  443.     
  444.     startFetchingFeeds : function () {
  445.         if (this.DEBUG) this.logMessage("Updating feeds " + new Date().toString());
  446.         
  447.         if (this.disabled) {
  448.             return;
  449.         }
  450.         
  451.         var ignore = this.readIgnoreFile();
  452.  
  453.         this.internalPause = true;
  454.  
  455.         for (var i = this.toolbar.childNodes.length - 1; i >= 0; i--){
  456.             if (this.toolbar.childNodes[i].nodeName == 'toolbarbutton'){
  457.                 if (this.inArray(ignore, this.toolbar.childNodes[i].feedURL)){
  458.                     this.toolbar.removeChild(this.toolbar.childNodes[i]);
  459.                 }
  460.             }
  461.         }
  462.  
  463.         this.checkForEmptiness();
  464.  
  465.         this.internalPause = false;
  466.  
  467.         RSSTICKER.feedsToFetch = [];
  468.         RSSTICKER.feedIndex = 0;
  469.         
  470.         var anno = Components.classes["@mozilla.org/browser/annotation-service;1"].getService(Components.interfaces.nsIAnnotationService);
  471.         var livemarkIds = anno.getItemsWithAnnotation("livemark/feedURI", {});
  472.  
  473.         for (var i = 0; i < livemarkIds.length; i++){
  474.             var feedURL = RSSTICKER.livemarkService.getFeedURI(livemarkIds[i]).spec;
  475.             var feedName = RSSTICKER.bookmarkService.getItemTitle(livemarkIds[i]);
  476.             
  477.             if (!this.inArray(ignore, feedURL)){
  478.                 RSSTICKER.feedsToFetch.push({ name : feedName, feed : feedURL, livemarkId : livemarkIds[i] });
  479.             }
  480.         }
  481.         
  482.         if (RSSTICKER.feedsToFetch.length == 0) {
  483.             RSSTICKER.notifyNoFeeds();
  484.         }
  485.         
  486.         setTimeout(function () {
  487.             RSSTICKER.setReloadInterval(RSSTICKER.prefs.getIntPref("updateFrequency"));
  488.         }, 5000);
  489.     },
  490.     
  491.     notifyNoFeeds : function () {
  492.         var showWindow = true;
  493.         
  494.         try {
  495.             showWindow = !this.prefs.getBoolPref("noFeedsFoundFlag.1.7");
  496.         } catch (e) {
  497.         }
  498.         
  499.         if (showWindow){
  500.             var url = "chrome://rss-ticker/content/noFeedsFound.xul";
  501.             var browser = gBrowser;
  502.             var theTab = browser.addTab(url);
  503.             browser.selectedTab = theTab;
  504.         }
  505.     },
  506.     
  507.     setReloadInterval : function (minutes) {
  508.         clearTimeout(RSSTICKER.feedUpdateTimeout);
  509.         
  510.         var numFeeds = RSSTICKER.feedsToFetch.length;
  511.         var interval = minutes;
  512.         
  513.         if (numFeeds == 0) {
  514.             numFeeds = interval;
  515.         }
  516.         
  517.         RSSTICKER.secondsBetweenFeeds = Math.ceil((interval * 60) / numFeeds);
  518.         
  519.         // Check if it's been more than $minutes minutes since the last full update.
  520.         var lastUpdate = this.prefs.getIntPref("lastUpdate") * 1000; 
  521.         var now = new Date().getTime();
  522.         
  523.         var minutesSince = (now - lastUpdate) / 1000 / 60;
  524.         
  525.         if ((minutes != 0) && (minutesSince > minutes)) {
  526.             RSSTICKER.updateAllFeeds();
  527.         }
  528.         else {
  529.             RSSTICKER.updateAFeed();
  530.         }
  531.     },
  532.     
  533.     updateAFeed : function (indexOverride) {
  534.         function setTimeoutForNext() {
  535.             if (RSSTICKER.rapidUpdate) {
  536.                 var interval = 0.5;
  537.             }
  538.             else {
  539.                 if (RSSTICKER.secondsBetweenFeeds == 0) {
  540.                     var interval = 60;
  541.                 }
  542.                 else {
  543.                     var interval = RSSTICKER.secondsBetweenFeeds;
  544.                 }
  545.             }
  546.             
  547.             RSSTICKER.feedUpdateTimeout = setTimeout(function () { RSSTICKER.updateAFeed(); }, interval * 1000);
  548.         }
  549.         
  550.         clearTimeout(RSSTICKER.feedUpdateTimeout);
  551.  
  552.         if (this.disabled || RSSTICKER.feedsToFetch.length == 0 || (!RSSTICKER.rapidUpdate && RSSTICKER.secondsBetweenFeeds == 0)){
  553.             setTimeoutForNext();
  554.             return;
  555.         }
  556.         
  557.         if (RSSTICKER.rapidUpdate) {
  558.             RSSTICKER.rapidUpdate--;
  559.             
  560.             if (!RSSTICKER.rapidUpdate) {
  561.                 RSSTICKER.stopUpdate();
  562.             }
  563.         }
  564.         
  565.         if (RSSTICKER.feedIndex >= RSSTICKER.feedsToFetch.length) {
  566.             RSSTICKER.feedIndex = 0;
  567.         }
  568.                 
  569.         var feedIndex = RSSTICKER.feedIndex;
  570.         
  571.         if (indexOverride) {
  572.             feedIndex = indexOverride;
  573.         }
  574.         else {
  575.             ++RSSTICKER.feedIndex;
  576.         }
  577.         
  578.         if (feedIndex == 0) {
  579.             this.prefs.setIntPref("lastUpdate", Math.round(new Date().getTime() / 1000));
  580.         }
  581.         
  582.         var feed = RSSTICKER.feedsToFetch[feedIndex];
  583.         
  584.         var url = feed.feed;
  585.         
  586.         /* START FEED FETCH HERE */
  587.         
  588.         if (this.DEBUG) this.logMessage("Loading " + url);
  589.         
  590.         var req = new XMLHttpRequest();
  591.         req.parent = this;
  592.         req.parent.addLoadingNotice("Updating " + feed.name + " (" + parseInt(RSSTICKER.feedIndex + 1, 10) + ")...");
  593.         
  594.         RSSTICKER.currentRequest = req;
  595.         
  596.         try {
  597.             req.open("GET", url, true);
  598.             
  599.             req.onreadystatechange = function (event) {
  600.                 if (req.readyState == 4) {
  601.                     clearTimeout(req.parent.loadTimer);
  602.                     
  603.                     req.parent.currentRequest = null;
  604.                     setTimeoutForNext();
  605.                     
  606.                     try {
  607.                         if (req.status == 200){
  608.                             var feedOb = null;
  609.                             
  610.                             try {
  611.                                 req.parent.queueForParsing(req.responseText.replace(/^\s\s*/, '').replace(/\s\s*$/, ''), url);
  612.                             } catch (e) {
  613.                                 // Parse error
  614.                             }
  615.                         }
  616.                         else {
  617.                         }
  618.                     }
  619.                     catch (e) {
  620.                     }
  621.                 }
  622.             };
  623.             
  624.             req.send(null);
  625.             RSSTICKER.loadTimer = setTimeout(function () { RSSTICKER.killCurrentRequest(); }, 1000 * 15);
  626.         }
  627.         catch (e) {
  628.             setTimeoutForNext();
  629.         }
  630.         
  631.         this.checkForEmptiness();
  632.         this.tick();
  633.     },
  634.     
  635.     removeFeed : function (livemarkId) {
  636.         var len = RSSTICKER.feedsToFetch.length;
  637.         
  638.         for (var i = 0; i < len; i++) {
  639.             if (RSSTICKER.feedsToFetch[i].livemarkId == livemarkId) {
  640.                 var label = RSSTICKER.feedsToFetch[i].name;
  641.                 RSSTICKER.feedsToFetch.splice(i, 1);
  642.  
  643.                 for (var i = this.toolbar.childNodes.length - 1; i >= 0; i--){
  644.                     var item = this.toolbar.childNodes[i];
  645.                     
  646.                     if (item.nodeName == 'toolbarbutton') {
  647.                         if (item.feed == label) {
  648.                             this.toolbar.removeChild(item);
  649.                         }
  650.                     }
  651.                 }
  652.                 
  653.                 return true;
  654.             }
  655.         }
  656.         
  657.         return false;
  658.     },
  659.  
  660.     updateSingleFeed : function (livemarkId) {
  661.         var feedURL = RSSTICKER.livemarkService.getFeedURI(livemarkId).spec;
  662.         var feedName = RSSTICKER.bookmarkService.getItemTitle(livemarkId);
  663.         
  664.         RSSTICKER.feedsToFetch.push({ name : feedName, feed : feedURL, livemarkId : livemarkId });
  665.         RSSTICKER.updateAFeed(RSSTICKER.feedsToFetch.length - 1);
  666.     },
  667.  
  668.     rapidUpdate : 0,
  669.     
  670.     updateAllFeeds : function () {
  671.         RSSTICKER.rapidUpdate = RSSTICKER.feedsToFetch.length;
  672.         RSSTICKER.updateAFeed();
  673.     },
  674.     
  675.     stopUpdate : function () {
  676.         RSSTICKER.rapidUpdate = 0;
  677.         RSSTICKER.killCurrentRequest();
  678.     },
  679.     
  680.     killCurrentRequest : function () {
  681.         try { RSSTICKER.currentRequest.abort(); } catch (noCurrentRequest) { }
  682.     },
  683.     
  684.     queueForParsing : function (feedText, feedURL) {
  685.         var data = feedText;
  686.         var uri = RSSTICKER.ioService.newURI(feedURL, null, null);
  687.  
  688.         if (data.length) {
  689.             var parser = Components.classes["@mozilla.org/feed-processor;1"]
  690.                             .createInstance(Components.interfaces.nsIFeedProcessor);
  691.             var listener = new TickerParseListener();
  692.  
  693.             try {
  694.                 parser.listener = listener;
  695.                 parser.parseFromString(data, uri);
  696.             } catch (e) {
  697.                 throw (e);
  698.             }
  699.         }
  700.  
  701.         return this;
  702.     },
  703.     
  704.     writeFeed : function (feed) {
  705.         var doTick, i, j;
  706.         
  707.         if (this.disabled) {
  708.             return;
  709.         }
  710.         
  711.         var feedItems = feed.items;
  712.         
  713.         this.internalPause = true;
  714.         
  715.         // Remove items that are no longer in the feed.
  716.         for (var i = this.toolbar.childNodes.length - 1; i >= 0; i--){
  717.             var item = this.toolbar.childNodes[i];
  718.             
  719.             if ((item.nodeName == 'toolbarbutton') && (item.feed == feed.label)){
  720.                 var itemFound = false;
  721.                 
  722.                 for (var j = 0; j < feedItems.length; j++){
  723.                     if (feedItems[j].uri == item.uri){
  724.                         itemFound = true;
  725.                         break;
  726.                     }
  727.                 }
  728.                 
  729.                 if (!itemFound){
  730.                     this.toolbar.removeChild(item);
  731.                 }
  732.             }
  733.         }
  734.         
  735.         var itemsShowing = this.itemsInTicker(feed.label);
  736.         
  737.         for (j = 0; j < feedItems.length; j++){
  738.             if (!document.getElementById("RSSTICKER" + feedItems[j].uri)){
  739.                 if (this.limitItemsPerFeed && (this.itemsPerFeed <= itemsShowing.length)){
  740.                     // Determine if this item is newer than the oldest item showing.
  741.                     if ((this.itemsPerFeed > 0) && feedItems[j].published && itemsShowing[0].published && (feedItems[j].published > itemsShowing[0].published)){
  742.                         this.toolbar.removeChild(document.getElementById("RSSTICKER" + itemsShowing[0].href));
  743.                         itemsShowing.shift();
  744.                     }
  745.                     else {                    
  746.                         continue;
  747.                     }
  748.                 }
  749.  
  750.                 var itemIsVisited = this.history.isVisitedURL(feedItems[j].uri, feedItems[j].id, 1);
  751.  
  752.                 if (itemIsVisited && this.hideVisited){
  753.                     continue;
  754.                 }
  755.  
  756.                 doTick = true;
  757.  
  758.                 feedItems[j].description = feedItems[j].description.replace(/<[^>]+>/g, "");
  759.  
  760.                 if ((feedItems[j].label == '') && (feedItems[j].description != '')){
  761.                     if (feedItems[j].description.length > 40){
  762.                         feedItems[j].label = feedItems[j].description.substr(0,40) + "...";
  763.                     }
  764.                     else {
  765.                         feedItems[j].label = feedItems[j].description;
  766.                     }
  767.                 }
  768.  
  769.                 var tbb = this.ce('toolbarbutton');
  770.                 tbb.uri = feedItems[j].uri;
  771.                 tbb.id = "RSSTICKER" + feedItems[j].uri;
  772.  
  773.                 tbb.setAttribute("label",feedItems[j].label);
  774.                 tbb.setAttribute("tooltip","RSSTICKERTooltip");
  775.                 tbb.setAttribute("image",feedItems[j].image);
  776.                 tbb.setAttribute("contextmenu","RSSTICKERItemCM");
  777.  
  778.                 tbb.setAttribute("onclick","return RSSTICKER.onTickerItemClick(event, this.uri, this);");
  779.  
  780.                 if (this.displayWidth.limitWidth){
  781.                     if (this.displayWidth.isMaxWidth){
  782.                         tbb.style.maxWidth = this.displayWidth.itemWidth + "px";
  783.                     }
  784.                     else {
  785.                         tbb.style.width = this.displayWidth.itemWidth + "px";
  786.                     }
  787.                 }
  788.  
  789.                 tbb.description = feedItems[j].description;
  790.                 tbb.visited = itemIsVisited;
  791.  
  792.                 tbb.feed = feed.label;
  793.                 tbb.feedURL = feed.uri;
  794.                 tbb.href = feedItems[j].uri;
  795.                 tbb.parent = this;
  796.                 tbb.published = feedItems[j].published;
  797.                 tbb.guid = feedItems[j].id;
  798.  
  799.                 if (this.hideVisited){
  800.                     tbb.markAsRead = function (addToHist, dontAdjustSpacer) {
  801.                         this.parentNode.removeChild(this);
  802.                         this.visited = true;
  803.  
  804.                         if (!dontAdjustSpacer) this.parent.adjustSpacerWidth();
  805.  
  806.                         if (addToHist){
  807.                             this.parent.history.addToHistory(this.guid);
  808.                         }
  809.  
  810.                         this.parent.checkForEmptiness();
  811.                     };
  812.                 }
  813.                 else if (this.boldUnvisited){
  814.                     if (!itemIsVisited){
  815.                         tbb.style.fontWeight = 'bold';
  816.                     }
  817.  
  818.                     tbb.markAsRead = function (addToHist, dontAdjustSpacer) {
  819.                         this.style.fontWeight = '';
  820.                         this.visited = true;
  821.  
  822.                         if (!dontAdjustSpacer) this.parent.adjustSpacerWidth();
  823.  
  824.                         if (addToHist){
  825.                             this.parent.history.addToHistory(this.guid);
  826.                         }
  827.                     };
  828.                 }
  829.                 else {
  830.                     tbb.markAsRead = function (addToHist, dontAdjustSpacer) {
  831.                         this.visited = true;
  832.                         if (addToHist){
  833.                             this.parent.history.addToHistory(this.guid);
  834.                         }
  835.                     };
  836.                 }
  837.  
  838.                 if (this.boldUnvisited){
  839.                     // Don't move this.
  840.                     if (!itemIsVisited){
  841.                         tbb.style.fontWeight = 'bold';
  842.                     }
  843.                 }
  844.                 
  845.                 tbb.onContextOpen = function (target) {
  846.                     var url = this.href;
  847.                     
  848.                     if (url.indexOf("oneriot.com") != -1 && url.indexOf("86f2f5da-3b24-4a87-bbb3-1ad47525359d") != -1) {
  849.                         var timestamp = new Date();
  850.                         timestamp = timestamp.getTime();
  851.                         
  852.                         if (RSSTICKER.lastOneRiotTimestamp > (timestamp - 1100)) {
  853.                             url = url.split("&")[0];
  854.                         }
  855.                         else {
  856.                             RSSTICKER.lastOneRiotTimestamp = timestamp;
  857.                         }
  858.                     }
  859.                     
  860.                     if (!target) {
  861.                         window._content.document.location.href = url;
  862.                     }
  863.                     else if (target == 'window'){
  864.                         window.open(url);
  865.                     }
  866.                     else if (target == 'tab') {
  867.                         this.parent.browser.openInNewTab(url);
  868.                     }
  869.                     
  870.                     this.markAsRead(true);
  871.                 };
  872.  
  873.                 // Determine where to add the item
  874.  
  875.                 if (this.randomizeItems){
  876.                     if (this.toolbar.childNodes.length == 1){
  877.                         // Only the spacer is showing
  878.                         this.toolbar.appendChild(tbb);
  879.                     }
  880.                     else {
  881.                         if ((this.toolbar.firstChild.nodeName == 'spacer') && ((this.currentFirstItemMargin * -1) < (this.toolbar.firstChild.nextSibling.boxObject.width))){
  882.                             var randomPlace = Math.floor(Math.random() * (this.toolbar.childNodes.length - 1)) + 1;
  883.                         }
  884.                         else {
  885.                             // Add after the 5th one just to avoid some jumpiness
  886.                             var randomPlace = Math.floor(Math.random() * (this.toolbar.childNodes.length - 1)) + 6;
  887.                         }
  888.  
  889.                         if (randomPlace >= this.toolbar.childNodes.length){
  890.                             this.toolbar.appendChild(tbb);
  891.                         }
  892.                         else {
  893.                             this.toolbar.insertBefore(tbb, this.toolbar.childNodes[randomPlace]);
  894.                         }
  895.                     }
  896.                 }
  897.                 else {
  898.                     // Check for another item from this feed, if so place at end of that feed.
  899.  
  900.                     if (itemsShowing.length > 0){
  901.                         for (i = this.toolbar.childNodes.length - 1; i >= 0; i--){
  902.                             if (this.toolbar.childNodes[i].nodeName == 'toolbarbutton'){
  903.                                 if (this.toolbar.childNodes[i].feed == tbb.feed){
  904.                                     if (i == (this.toolbar.childNodes.length - 1)){
  905.                                         this.toolbar.appendChild(tbb);
  906.                                         addedButton = true;
  907.                                     }
  908.                                     else {
  909.                                         this.toolbar.insertBefore(tbb, this.toolbar.childNodes[i+1]);
  910.                                         addedButton = true;
  911.                                     }
  912.  
  913.                                     break;
  914.                                 }
  915.                             }
  916.                         }
  917.                     }
  918.                     else {
  919.                         // None of this feed is showing; add after another feed.
  920.                         if ((this.toolbar.firstChild.nodeName == 'spacer') || (this.toolbar.lastChild.nodeName == 'spacer')){
  921.                             this.toolbar.appendChild(tbb);
  922.                         }
  923.                         else {
  924.                             if (this.toolbar.firstChild.feed != this.toolbar.lastChild.feed){
  925.                                 // We're in luck - a feed just finished scrolling
  926.                                 this.toolbar.appendChild(tbb);
  927.                             }
  928.                             else {
  929.                                 var addedButton = false;
  930.  
  931.                                 for (i = this.toolbar.childNodes.length - 2; i >= 0; i--){
  932.                                     if (this.toolbar.childNodes[i].nodeName == 'spacer'){
  933.                                         this.toolbar.insertBefore(tbb, this.toolbar.childNodes[i+1]);
  934.                                         addedButton = true;
  935.                                         break;
  936.                                     }
  937.                                     else if (this.toolbar.childNodes[i].feed != this.toolbar.childNodes[i+1].feed){
  938.                                         this.toolbar.insertBefore(tbb, this.toolbar.childNodes[i+1]);
  939.                                         addedButton = true;
  940.                                         break;
  941.                                     }
  942.                                 }
  943.  
  944.                                 if (!addedButton){
  945.                                     this.toolbar.appendChild(tbb);
  946.                                 }
  947.                             }
  948.                         }
  949.                     }
  950.                 }
  951.                 
  952.                 itemsShowing.push(tbb);
  953.                 
  954.                 itemsShowing.sort(RSSTICKER.sortByPubDate);
  955.             }
  956.         }
  957.         
  958.         this.internalPause = false;
  959.         
  960.         this.adjustSpacerWidth();
  961.         this.checkForEmptiness();
  962.         this.tick();
  963.     },
  964.     
  965.     onTickerItemClick : function (event, url, node) {        
  966.         if (event.ctrlKey) {
  967.             node.markAsRead(true); 
  968.             return false;
  969.         }
  970.         else if (event.which == 3){
  971.             // Discard right-clicks
  972.             return;
  973.         }
  974.         else {
  975.             if (url.indexOf("oneriot.com") != -1 && url.indexOf("86f2f5da-3b24-4a87-bbb3-1ad47525359d") != -1) {
  976.                 var timestamp = new Date();
  977.                 timestamp = timestamp.getTime();
  978.                 
  979.                 if (RSSTICKER.lastOneRiotTimestamp > (timestamp - 1100)) {
  980.                     url = url.split("&")[0];
  981.                 }
  982.                 else {
  983.                     RSSTICKER.lastOneRiotTimestamp = timestamp;
  984.                 }
  985.             }
  986.             
  987.             if (event.which == 4 || event.shiftKey){
  988.                 // Shift
  989.                 window.open(url);
  990.             }
  991.             else {
  992.                 // Left-click
  993.                 this.launchUrl(url, event);
  994.                 node.markAsRead(true);
  995.             }
  996.         }
  997.     },
  998.     
  999.     launchUrl : function (url, event) {
  1000.         if (this.prefs.getBoolPref("alwaysOpenInNewTab") || (event.which == 2) || (event.which == 1 && (event.ctrlKey || event.metaKey) && (event.ctrlKey || event.metaKey))){
  1001.             this._addTab(url);
  1002.         }
  1003.         else if (event.which == 1 || (event.which == 13 && !event.shiftKey)){
  1004.             this._inTab(url);
  1005.         }
  1006.         else if (event.which == 4 || (event.which == 13 && event.shiftKey)){
  1007.             window.open(url);
  1008.         }
  1009.     },
  1010.     
  1011.     _addTab : function (url) {
  1012.         var browser = gBrowser;
  1013.         var theTab = browser.addTab(url);
  1014.         
  1015.         var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  1016.             
  1017.         var loadInBackground = false;
  1018.             
  1019.         try {
  1020.             loadInBackground = prefs.getBoolPref("browser.tabs.loadInBackground");
  1021.         } catch (e) {
  1022.         }
  1023.             
  1024.         if (!loadInBackground){
  1025.             browser.selectedTab = theTab;
  1026.         }
  1027.     },
  1028.     
  1029.     _inTab : function (url) {
  1030.         content.document.location.href = url;
  1031.     },
  1032.     
  1033.     adjustSpacerWidth : function () {
  1034.         if (this.toolbar && !this.disabled){
  1035.             var extraPadding;
  1036.             
  1037.             try {
  1038.                 if (this.displayWidth.limitWidth){
  1039.                     extraPadding = this.displayWidth.itemWidth;
  1040.                 }
  1041.                 else {
  1042.                     extraPadding = 250;
  1043.                 }
  1044.                 
  1045.                 var windowWidth = parseInt(this.ticker.boxObject.width);
  1046.                 
  1047.                 var tickerWidth = 0;
  1048.                 
  1049.                 for (var i = 0; i < this.toolbar.childNodes.length; i++){
  1050.                     if (this.toolbar.childNodes[i].nodeName == 'toolbarbutton'){
  1051.                         tickerWidth += this.toolbar.childNodes[i].boxObject.width;
  1052.                     }
  1053.                 }
  1054.                 
  1055.                 var spacerWidth;
  1056.                 
  1057.                 if (parseInt(windowWidth) > parseInt(tickerWidth - extraPadding)) {
  1058.                     spacerWidth = parseInt(windowWidth) - parseInt(tickerWidth) + parseInt(extraPadding);
  1059.                 }
  1060.                 else {
  1061.                     spacerWidth = 0;
  1062.                 }
  1063.                 
  1064.                 this.toolbar.spacer.style.width = spacerWidth + "px";
  1065.             } catch (e) {
  1066.                 // Tried to adjust spacer when there wasn't one.
  1067.                 // Could happen depending on when the disable button was pressed
  1068.                 if (this.DEBUG) this.logMessage(e);
  1069.             }
  1070.         }
  1071.     },
  1072.     
  1073.     tick : function () {
  1074.         if (this.disabled) {
  1075.             return;
  1076.         }
  1077.         
  1078.         clearTimeout(this.tickTimer);
  1079.         
  1080.         if (this.internalPause){
  1081.             this.tickTimer = setTimeout(function () { RSSTICKER.tick(); }, this.tickLength);
  1082.         }
  1083.         else {
  1084.             var node, nodeWidth, marginLeft;
  1085.             
  1086.             if (this.mouseOverFlag){
  1087.                 if (this.toolbar.childNodes.length > 1){
  1088.                     if (this.currentFirstItemMargin <= (this.toolbar.firstChild.boxObject.width * -1)){
  1089.                         node = this.toolbar.firstChild;
  1090.                         this.toolbar.removeChild(node);
  1091.                         this.currentFirstItemMargin = 0;
  1092.                         node.style.marginLeft = '0px';
  1093.                         this.toolbar.appendChild(node);
  1094.                         
  1095.                         if (node.nodeName == 'toolbarbutton' && !node.visited){
  1096.                             if (this.history.isVisitedURL(node.href, node.guid, 2)){
  1097.                                 node.markAsRead(true);
  1098.                             }
  1099.                         }
  1100.                     }
  1101.                     else if (this.currentFirstItemMargin > 0){
  1102.                         // Move the last child back to the front.
  1103.                         node = this.toolbar.lastChild;
  1104.                         nodeWidth = node.boxObject.width;
  1105.                         this.toolbar.removeChild(node);
  1106.                         
  1107.                         // Set the correct margins
  1108.                         marginLeft = parseInt((0 - nodeWidth) + this.currentFirstItemMargin);
  1109.                         
  1110.                         node.style.marginLeft = marginLeft + "px";
  1111.                         this.currentFirstItemMargin = marginLeft;
  1112.                         this.toolbar.firstChild.style.marginLeft = 0;
  1113.                         this.toolbar.insertBefore(node, this.toolbar.firstChild);
  1114.                     }
  1115.                 }
  1116.                 
  1117.                 this.tickTimer = setTimeout(function () { RSSTICKER.tick(); }, this.tickLength);
  1118.             }
  1119.             else {
  1120.                 if (this.toolbar.childNodes.length > 1){
  1121.                     if (this.currentFirstItemMargin <= (this.toolbar.firstChild.boxObject.width * -1)){
  1122.                         node = this.toolbar.firstChild;
  1123.                         this.toolbar.removeChild(node);
  1124.                         this.currentFirstItemMargin = 0;
  1125.                         node.style.marginLeft = '0px';
  1126.                         this.toolbar.appendChild(node);
  1127.                         
  1128.                         if (node.nodeName == 'toolbarbutton' && !node.visited){
  1129.                             if (this.history.isVisitedURL(node.href, node.guid, 3)){
  1130.                                 node.markAsRead(true);
  1131.                             }
  1132.                         }
  1133.                     }
  1134.                     else if (this.currentFirstItemMargin > 0){
  1135.                         // Move the last child back to the front.
  1136.                         node = this.toolbar.lastChild;
  1137.                         this.toolbar.removeChild(node);
  1138.                         
  1139.                         // Set the correct margins
  1140.                         nodeWidth = node.boxObject.width;
  1141.                         marginLeft = parseInt((0 - nodeWidth) + this.currentFirstItemMargin);
  1142.  
  1143.                         node.style.marginLeft = marginLeft + "px";
  1144.                         this.currentFirstItemMargin = marginLeft;
  1145.                         this.toolbar.firstChild.style.marginLeft = 0;
  1146.                         this.toolbar.insertBefore(node, this.toolbar.firstChild);
  1147.                     }
  1148.                     else {
  1149.                         this.currentFirstItemMargin -= (200 / this.ticksPerItem);
  1150.                         this.toolbar.firstChild.style.marginLeft = this.currentFirstItemMargin + "px";
  1151.                     }
  1152.                 }
  1153.                 
  1154.                 this.tickTimer = setTimeout(function () { RSSTICKER.tick(); }, this.tickLength);
  1155.             }
  1156.         }
  1157.     },
  1158.     
  1159.     markAllAsRead : function (feed) {
  1160.         this.internalPause = true;
  1161.         
  1162.         for (var i = this.toolbar.childNodes.length - 1; i >= 0; i--){
  1163.             if (this.toolbar.childNodes[i].nodeName == 'toolbarbutton'){
  1164.                 if (!feed || (this.toolbar.childNodes[i].feed == feed)){
  1165.                     this.history.addToHistory(this.toolbar.childNodes[i].guid);
  1166.                     this.toolbar.childNodes[i].markAsRead(true, true);
  1167.                 }
  1168.             }
  1169.         }
  1170.         
  1171.         this.internalPause = false;
  1172.         
  1173.         this.adjustSpacerWidth();
  1174.     },
  1175.     
  1176.     openAllInTabs : function (unreadOnly, feed) {
  1177.         for (var i = this.toolbar.childNodes.length - 1; i >= 0; i--){
  1178.             if (this.toolbar.childNodes[i].nodeName == 'toolbarbutton'){
  1179.                 if ((!feed || (this.toolbar.childNodes[i].feed == feed)) &&    (!unreadOnly || (!this.toolbar.childNodes[i].visited))){
  1180.                     this.toolbar.childNodes[i].onContextOpen('tab');
  1181.                 }
  1182.             }
  1183.         }
  1184.     },
  1185.     
  1186.     // Each scroll of the mouse should move the ticker items 
  1187.     
  1188.     scrollTicker : function (event) {
  1189.         if (this.mouseOverFlag && !this.internalPause){
  1190.             if (this.toolbar.childNodes.length > 1){
  1191.                 if (event.detail > 0){
  1192.                     // Scroll Down
  1193.                     if (this.toolbar.firstChild){
  1194.                         this.currentFirstItemMargin -= 40;
  1195.                         this.toolbar.firstChild.style.marginLeft = this.currentFirstItemMargin + "px";
  1196.                     }
  1197.                 }
  1198.                 else if (event.detail < 0){
  1199.                     // Scroll Up
  1200.                     if (this.toolbar.firstChild){
  1201.                         this.currentFirstItemMargin += 40;
  1202.                         this.toolbar.firstChild.style.marginLeft = this.currentFirstItemMargin + "px";
  1203.                         
  1204.                         if (this.currentFirstItemMargin > 0){
  1205.                             // Move the last child back to the front.
  1206.                             var node = this.toolbar.lastChild;
  1207.                             var nodeWidth = node.boxObject.width;
  1208.                             this.toolbar.removeChild(node);
  1209.                             
  1210.                             // Set the correct margins
  1211.                             var marginLeft = (0 - nodeWidth) + this.currentFirstItemMargin;
  1212.                             
  1213.                             node.style.marginLeft = marginLeft + "px";
  1214.                             this.currentFirstItemMargin = marginLeft;
  1215.                             this.toolbar.firstChild.style.marginLeft = 0;
  1216.                             this.toolbar.insertBefore(node, this.toolbar.firstChild);
  1217.                         }
  1218.                     }
  1219.                 }
  1220.             }
  1221.         }
  1222.     },
  1223.     
  1224.     toggleDisabled : function () {
  1225.         this.prefs.setBoolPref("disabled", !this.prefs.getBoolPref("disabled"));
  1226.     },
  1227.     
  1228.     fillInTooltip : function (item, tt){
  1229.         var maxLength = 60;
  1230.         
  1231.         var descr = item.description;
  1232.         var title = item.getAttribute("label");
  1233.         var url = item.href;
  1234.         var feedName = item.feed;
  1235.         
  1236.         if (title.length > maxLength){
  1237.             title = title.substring(0,maxLength) + "...";
  1238.         }
  1239.         
  1240.         if (url.length > maxLength){
  1241.             url = url.substring(0,maxLength) + "...";
  1242.         }
  1243.         
  1244.         var image = item.getAttribute("image");
  1245.         
  1246.         tt.removeAttribute("height");
  1247.         tt.removeAttribute("width");
  1248.         
  1249.         document.getElementById("RSSTICKERTooltipImage").src = image;
  1250.         
  1251.         document.getElementById("RSSTICKERTooltipURL").value = this.strings.getString("URL") + ": " + url;
  1252.         
  1253.         var maxw = document.getElementById("RSSTICKERTooltipURL").boxObject.width;
  1254.         
  1255.         document.getElementById("RSSTICKERTooltipFeedName").value = feedName;
  1256.         
  1257.         maxw = Math.max(maxw, document.getElementById("RSSTICKERTooltipFeedName").boxObject.width);
  1258.         
  1259.         if (title != ''){
  1260.             document.getElementById("RSSTICKERTooltipName").value = title;;
  1261.             document.getElementById("RSSTICKERTooltipName").style.display = '';
  1262.             
  1263.             maxw = Math.max(document.getElementById("RSSTICKERTooltipName").boxObject.width, maxw);
  1264.         }
  1265.         else {
  1266.             document.getElementById("RSSTICKERTooltipName").style.display = 'none';
  1267.         }
  1268.                 
  1269.         if (descr != ''){
  1270.             for (var i = 0; i < document.getElementById("RSSTICKERTooltipSummary").childNodes.length; i++){
  1271.                 document.getElementById("RSSTICKERTooltipSummary").removeChild(document.getElementById("RSSTICKERTooltipSummary").lastChild);
  1272.             }
  1273.             
  1274.             if (descr.length > 200){
  1275.                 descr = descr.substring(0, descr.indexOf(" ",200)) + "...";
  1276.             }
  1277.             
  1278.             var text = document.createTextNode(descr);
  1279.             
  1280.             document.getElementById("RSSTICKERTooltipSummary").appendChild(text);
  1281.             document.getElementById("RSSTICKERTooltipSummaryGroupbox").setAttribute("hidden",false);
  1282.         }
  1283.         else {
  1284.             document.getElementById("RSSTICKERTooltipSummaryGroupbox").setAttribute("hidden",true);
  1285.         }
  1286.   
  1287.         return true;
  1288.     },
  1289.     
  1290.     logMessage : function (message) {
  1291.         var consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
  1292.         consoleService.logStringMessage("RSSTICKER: " + message);
  1293.     },
  1294.     
  1295.     theFile : null,
  1296.     theDB : null,
  1297.     
  1298.     getDB : function () {
  1299.         if (!this.theFile) {
  1300.             this.theFile = Components.classes["@mozilla.org/file/directory_service;1"]
  1301.                              .getService(Components.interfaces.nsIProperties)
  1302.                              .get("ProfD", Components.interfaces.nsIFile);
  1303.             this.theFile.append("rssticker.sqlite");
  1304.         }
  1305.         
  1306.         if (!this.theDB) {
  1307.             this.theDB = Components.classes["@mozilla.org/storage/service;1"]
  1308.                          .getService(Components.interfaces.mozIStorageService).openDatabase(this.theFile);
  1309.         }
  1310.         
  1311.         return this.theDB;
  1312.     },
  1313.     
  1314.     closeDB : function () {
  1315.         this.theDB.close();
  1316.         delete this.theDB;
  1317.         this.theDB = null;
  1318.     },
  1319.     
  1320.     history : {
  1321.         hService : Components.classes["@mozilla.org/browser/global-history;2"].getService(Components.interfaces.nsIGlobalHistory2),
  1322.         ioService : Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService),
  1323.         
  1324.         URI : null,
  1325.         
  1326.         isVisitedURL : function(url, guid){
  1327.             try {
  1328.                 RSSTICKER.history.URI = this.ioService.newURI(url, null, null);
  1329.                 var visited = RSSTICKER.history.hService.isVisited(RSSTICKER.history.URI);
  1330.                 
  1331.                 var db = RSSTICKER.getDB();
  1332.                 
  1333.                 if (!visited) {
  1334.                     var select = db.createStatement("SELECT id FROM history WHERE id=?1");
  1335.                     select.bindStringParameter(0, guid);
  1336.  
  1337.                     try {
  1338.                         while (select.executeStep()) {
  1339.                             visited = true;
  1340.                             break;
  1341.                         }
  1342.                     } catch (e) {
  1343.                         RSSTICKER.logMessage(e);
  1344.                     } finally {    
  1345.                         select.reset();
  1346.                     }
  1347.                 }
  1348.                 else {
  1349.                     // Add to DB
  1350.                     var insert = db.createStatement("INSERT INTO history (id, date) VALUES (?1, ?2)");
  1351.                     insert.bindUTF8StringParameter(0, guid);
  1352.                     insert.bindInt64Parameter(1, (new Date().getTime()));
  1353.                     
  1354.                     try { insert.execute(); } catch (alreadyExists) { }
  1355.                 }
  1356.                 
  1357.                 return visited;
  1358.             } catch (e) {
  1359.                 // Malformed URI, probably
  1360.                 RSSTICKER.logMessage(e + " " + url);
  1361.                 return false;
  1362.             }
  1363.         },
  1364.         
  1365.         
  1366.         addToHistory : function (guid) {
  1367.             var db = RSSTICKER.getDB();
  1368.             
  1369.             // Add to DB
  1370.             var insert = db.createStatement("INSERT INTO history (id, date) VALUES (?1, ?2)");
  1371.             insert.bindUTF8StringParameter(0, guid);
  1372.             insert.bindInt64Parameter(1, (new Date().getTime()));
  1373.             
  1374.             try { insert.execute(); } catch (alreadyExists) { }
  1375.         },
  1376.     },
  1377.     
  1378.     clipboard : {
  1379.         copyString : function (str){
  1380.             try {
  1381.                 var oClipBoard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
  1382.                 oClipBoard.copyString(str);
  1383.             } catch (e) {
  1384.             }
  1385.         }
  1386.     },
  1387.     
  1388.     browser : {
  1389.         openInNewTab : function(url){
  1390.             var browser = window.gBrowser || window.parent.gBrowser;
  1391.             
  1392.             var theTab = browser.addTab(url);
  1393.             
  1394.             var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  1395.             
  1396.             var loadInBackground = false;
  1397.             
  1398.             try {
  1399.                 loadInBackground = prefs.getBoolPref("browser.tabs.loadInBackground");
  1400.             } catch (e) {
  1401.             }
  1402.             
  1403.             if (!loadInBackground){
  1404.                 browser.selectedTab = theTab;
  1405.             }
  1406.         }
  1407.     },
  1408.     
  1409.     ce : function (name){
  1410.         return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", name);
  1411.     },
  1412.     
  1413.     observeFeed : function (url) {
  1414.         var feeds = this.readIgnoreFile();
  1415.         var newFeeds = [];
  1416.         
  1417.         var foundFeed = false;
  1418.         
  1419.         for (var i = 0; i < feeds.length; i++){
  1420.             if (feeds[i] == url){
  1421.                 foundFeed = true;
  1422.             }
  1423.             else {
  1424.                 newFeeds.push(feeds[i]);
  1425.             }
  1426.         }
  1427.     
  1428.         if (foundFeed){
  1429.             // Rewrite the ignore file.
  1430.             var DIR_SERVICE = new Components.Constructor("@mozilla.org/file/directory_service;1", "nsIProperties");
  1431.             var profilePath = (new DIR_SERVICE()).get("ProfD", Components.interfaces.nsIFile).path; 
  1432.         
  1433.             if (profilePath.search(/\\/) != -1) profilePath += "\\";
  1434.             else profilePath += "/";
  1435.                         
  1436.             var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
  1437.             file.initWithPath(profilePath + this.ignoreFilename);
  1438.  
  1439.             var outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance( Components.interfaces.nsIFileOutputStream );
  1440.  
  1441.             outputStream.init(file, 0x02 | 0x08 | 0x20, 0600, null);
  1442.         
  1443.             var data = "";
  1444.             
  1445.             for (var i = 0; i < newFeeds.length; i++){
  1446.                 data += newFeeds[i] + "\r\n";
  1447.             }
  1448.             
  1449.             this.writeBytes(outputStream, data);
  1450.             
  1451.             outputStream.close();
  1452.         }
  1453.     },
  1454.  
  1455.     writeBytes : function (outputStream, data) {
  1456.         var bytes, bytesWritten, bytesRemaining = data.length;
  1457.         var offset = 0;
  1458.         
  1459.         while (bytesRemaining) {
  1460.             bytesWritten = outputStream.write(data.substring(offset), bytesRemaining);
  1461.             bytesRemaining -= bytesWritten;
  1462.             offset += bytesWritten;
  1463.         }
  1464.     },
  1465.     
  1466.     ignoreFeed : function (url) {
  1467.         var feeds = this.readIgnoreFile();
  1468.         var foundFeed = false;
  1469.         
  1470.         for (var i = 0; i < feeds.length; i++){
  1471.             if (feeds[i] == url){
  1472.                 foundFeed = true;
  1473.                 break;
  1474.             }
  1475.         }
  1476.         
  1477.         if (!foundFeed){
  1478.             var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
  1479.             file.initWithPath(this.getProfilePath() + this.ignoreFilename);
  1480.  
  1481.             var outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance( Components.interfaces.nsIFileOutputStream );
  1482.             outputStream.init(file, 0x02 | 0x08 | 0x10, 0600, null);
  1483.         
  1484.             var data = url + "\r\n";
  1485.             
  1486.             this.writeBytes(outputStream, data);
  1487.             
  1488.             outputStream.close();
  1489.         }
  1490.     },
  1491.     
  1492.     readIgnoreFile : function () {
  1493.         var file, inputStream, lineStream, stillInFile, parts;
  1494.         var feeds = [];
  1495.         var line = { value: "" };
  1496.         
  1497.         file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
  1498.         
  1499.         file.initWithPath(this.getProfilePath() + this.ignoreFilename);
  1500.         
  1501.         if (!file.exists()) {
  1502.             // File doesn't exist yet
  1503.             return [];
  1504.         }
  1505.  
  1506.         inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
  1507.         inputStream.init(file, 0x01, 0600, null);
  1508.         
  1509.         lineStream = inputStream.QueryInterface(Components.interfaces.nsILineInputStream);
  1510.         
  1511.         do {
  1512.             stillInFile = lineStream.readLine(line);
  1513.             
  1514.             if (line.value == "") {
  1515.                 continue;
  1516.             }
  1517.             else {
  1518.                 feeds.push(line.value);
  1519.             }
  1520.         } while (stillInFile);
  1521.         
  1522.         lineStream.close();
  1523.         inputStream.close();
  1524.         
  1525.         return feeds;
  1526.     },
  1527.     
  1528.     getProfilePath : function () {
  1529.         if (!this.profilePath){
  1530.             var DIR_SERVICE = new Components.Constructor("@mozilla.org/file/directory_service;1", "nsIProperties");
  1531.             this.profilePath = (new DIR_SERVICE()).get("ProfD", Components.interfaces.nsIFile).path; 
  1532.         
  1533.             if (this.profilePath.search(/\\/) != -1) this.profilePath += "\\";
  1534.             else this.profilePath += "/";
  1535.         }
  1536.         
  1537.         return this.profilePath;
  1538.     },
  1539.     
  1540.     inArray : function (arr, needle){
  1541.         var i;
  1542.         
  1543.         for (i = 0; i < arr.length; i++){
  1544.             if (arr[i] == needle){
  1545.                 return true;
  1546.             }
  1547.         }
  1548.         
  1549.         return false;
  1550.     },
  1551.     
  1552.     itemsInTicker : function (feed) {
  1553.         var items = [];
  1554.         var ip = this.internalPause;
  1555.         
  1556.         this.internalPause = true;
  1557.         
  1558.         for (var i = this.toolbar.childNodes.length - 1; i >= 0; i--){
  1559.             var tbb = this.toolbar.childNodes[i];
  1560.             
  1561.             if (tbb.nodeName != "spacer" && tbb.feed == feed){
  1562.                 if (this.limitItemsPerFeed && (items.length == this.itemsPerFeed)){
  1563.                     this.toolbar.removeChild(tbb);
  1564.                 }
  1565.                 else {
  1566.                     items.push(tbb);
  1567.                 }
  1568.             }
  1569.         }
  1570.         
  1571.         // Sort the array by time
  1572.         
  1573.         items.sort(RSSTICKER.sortByPubDate);
  1574.         
  1575.         this.internalPause = ip;
  1576.         
  1577.         this.checkForEmptiness();
  1578.         
  1579.         return items;
  1580.     },
  1581.     
  1582.     checkForEmptiness : function(){
  1583.         if (this.toolbar) {
  1584.             if ((this.toolbar.childNodes.length <= 1) && (this.hideWhenEmpty)){
  1585.                 this.ticker.style.display = 'none';
  1586.                 this.toolbar.firstChild.style.marginLeft = '0px';
  1587.                 this.currentFirstItemMargin = 0;
  1588.                 this.mouseOverFlag = false;
  1589.             }
  1590.             else {
  1591.                 this.ticker.style.display = '';
  1592.                 
  1593.                 if (this.toolbar.childNodes.length <= 1){
  1594.                     document.getElementById("rss-ticker_cmd_openAllInTabs").setAttribute("disabled","true");
  1595.                     document.getElementById("rss-ticker_cmd_openUnreadInTabs").setAttribute("disabled","true");
  1596.                     document.getElementById("rss-ticker_cmd_markAllAsRead").setAttribute("disabled","true");
  1597.                 }
  1598.                 else {
  1599.                     document.getElementById("rss-ticker_cmd_openAllInTabs").removeAttribute("disabled","true");
  1600.                     document.getElementById("rss-ticker_cmd_openUnreadInTabs").removeAttribute("disabled","true");
  1601.                     document.getElementById("rss-ticker_cmd_markAllAsRead").removeAttribute("disabled","true");
  1602.                 }
  1603.             }
  1604.         }
  1605.     },
  1606.     
  1607.     sortByPubDate : function (a, b){
  1608.         var atime, btime;
  1609.  
  1610.         if (a.published){
  1611.             atime = a.published;
  1612.         }
  1613.  
  1614.         if (b.published){
  1615.             btime = b.published;
  1616.         }
  1617.  
  1618.         if (!atime && !btime){
  1619.             return 0;
  1620.         }
  1621.         else if (!btime){
  1622.             return 1;
  1623.         }
  1624.         else if (!atime){
  1625.             return -1;
  1626.         }
  1627.         else {
  1628.             return atime - btime;
  1629.         }
  1630.     }
  1631. };